package com.aabte.spring.annotation.config;

import com.aabte.spring.aop.LogAspects;
import com.aabte.spring.aop.MathCaculator;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.Interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.*;
import org.springframework.aop.Advisor;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.BeforeAdvice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.aspectj.AspectJAfterAdvice;
import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
import org.springframework.aop.aspectj.AspectJPointcutAdvisor;
import org.springframework.aop.aspectj.InstantiationModelAwarePointcutAdvisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator;
import org.springframework.aop.framework.*;
import org.springframework.aop.framework.adapter.AdvisorAdapter;
import org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor;
import org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor;
import org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.beans.factory.*;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.*;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.core.Ordered;

/**
 * AOP：指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。
 * <p>
 * 使用前先导入依赖包：spring-aspects
 * 定义一个业务逻辑类{@link MathCaculator#div}
 * 定义一个切面类{@link LogAspects}，实现通知方法
 *  通知方法：
 *      1、前置通知{@link Before}：在目标方法调用之前调用
 *      2、后置通知{@link After}：在目标方法调用结束之后调用，无论是否正常返回都会被调用
 *      3、返回通知{@link AfterReturning}：在目标方法正常返回之后调用
 *      4、异常通知{@link AfterThrowing}：在目标方法抛出异常以后调用
 *      5、环绕通知{@link Around}：手动调用目标方法（joinPoint.procced()）
 * 给切面类的目标方法标注何时何地被调用
 * 将切面类和业务逻辑类（目标方法所在类）都加入容器中
 * 在切面类标注{@link Aspect}注解，告诉Spring是个切面类
 * 在主配置类上标注{@link EnableAspectJAutoProxy}注解，开启基于注解的AOP模式
 *
 * AOP原理：（重点关注向容器中注册了什么组件，组件什么时候工作，组件的功能是什么）
 * {@link EnableAspectJAutoProxy}注解会先向IOC注入{@link AspectJAutoProxyRegistrar}组件，
 * 此组件实现了{@link ImportBeanDefinitionRegistrar}接口，
 * 通过其实现手动注入{@link AnnotationAwareAspectJAutoProxyCreator}组件的RootBeanDefinition对象
 *
 * {@link AnnotationAwareAspectJAutoProxyCreator}的继承关系
 *  {@link AspectJAwareAdvisorAutoProxyCreator}
 *      {@link AbstractAutoProxyCreator}
 *          implements {@link BeanFactoryAware}
 *          extends {@link ProxyProcessorSupport}
 *              extends {@link ProxyConfig}
 *              implements {@link Ordered}
 *              implements {@link BeanClassLoaderAware}
 *                  extends {@link Aware}
 *              implements {@link AopInfrastructureBean}
 *          implements {@link SmartInstantiationAwareBeanPostProcessor}
 *              {@link InstantiationAwareBeanPostProcessor}
 *                  {@link BeanPostProcessor}，最终实现了Bean后置处理器
 *
 *  1、传入配置类，创建IOC容器
 *  2、注册配置类、调用refresh刷新容器
 *  3、注册Bean后置处理器，用以拦截Bean的创建{@link org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors}
 *      1、从IOC容器中获取所有已经创建的后置处理器Bean的名称
 *      2、向IOC容器添加其它后置处理器
 *      3、优先注册实现了PriorityOrdered接口的BeanPostProcessor
 *      4、再给容器中注册实现了Ordered接口的BeanPostProcessor
 *      5、注册没有实现优先级接口的BeanPostProcessor
 *      6、注册BeanPostProcessor，实现上就是创建BeanPostProcessor对象，保存在容器中
 *          创建internalAutoProxyCreator的BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator对象)
 *          1、创建Bean的实例 {@link AbstractAutowireCapableBeanFactory#createBeanInstance}
 *          2、给Bean的属性赋值  {@link AbstractAutowireCapableBeanFactory#populateBean}
 *          3、初始化Bean {@link AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)}
 *              1、回调{@link Aware}接口方法 {@link AbstractAutowireCapableBeanFactory#invokeAwareMethods}
 *              2、回调所有后置处理器{@link BeanPostProcessor#postProcessBeforeInitialization(Object, String)}方法 {@link AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization}
 *              2、回调Bean的初始化方法 {@link AbstractAutowireCapableBeanFactory#invokeInitMethods}
 *              2、回调所有后置处理器{@link BeanPostProcessor#postProcessAfterInitialization(Object, String)}方法 {@link AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization}
 *          4、后置处理器创建成功{@link AnnotationAwareAspectJAutoProxyCreator}
 *      7、把BeanPostProcessor注册到BeanFactory中，到此{@link AnnotationAwareAspectJAutoProxyCreator}对象创建完成
 *  4、完成BeanFactory初始化工作，创建剩下的单例Bean {@link AbstractApplicationContext#finishBeanFactoryInitialization(ConfigurableListableBeanFactory)}
 *      1、从容器中获取所有Bean的名称，依次创建Bean对象
 *          {@link AbstractBeanFactory#getBean(String)}
 *          {@link AbstractBeanFactory#doGetObjectFromFactoryBean(FactoryBean, String)}
 *          {@link AbstractBeanFactory#getSingleton(String, ObjectFactory)} )}
 *      2、创建Bean
 *          1、先从缓存中获取当前Bean，如果能获取到，说明Bean是之前被创建过的，直接使用，否则再创建Bean并保存起来
 *          2、尝试创建Bean的代理对象，不为null直接使用代理对象 {@link AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation(String, RootBeanDefinition)}
 *              1、获取所有后置处理Bean {@link BeanPostProcessor}
 *              2、依次判断后置处理是否是{@link InstantiationAwareBeanPostProcessor}类型，
 *                 如果是回调{@link InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization(Object, String)}，
 *                 此方法如果返回代理对象，则调用{@link InstantiationAwareBeanPostProcessor#postProcessAfterInitialization(Object, String)}方法
 *          3、如果返回了代理对象，直接返回，如果没有则继续执行创建Bean逻辑 {@link AbstractAutowireCapableBeanFactory#doCreateBean(String, RootBeanDefinition, Object[])}
 *
 * {@link AnnotationAwareAspectJAutoProxyCreator}后置处理器：
 *  1、在创建每个Bean的时候，调用{@link AbstractAutoProxyCreator#postProcessBeforeInitialization(Object, String)}方法实现创建代理对象
 *      1、判断当前Bean目标对象是否需要增强（advisedBeans中保存了所有需要增强的Bean）
 *      2、判断是否需要增强
 *          1、判断当前Bean是否需要增强（advisedBeans中保存了所有需要增强的Bean）
 *          2、判断是否是基础类型或者是切面类，{@link AnnotationAwareAspectJAutoProxyCreator#isInfrastructureClass}如：{@link Before}、{@link Advice}、{@link Advisor}、{@link Pointcut}、{@link AopInfrastructureBean}、{@link Aspect}
 *      3、判断是否需要跳过 {@link AspectJAwareAdvisorAutoProxyCreator#shouldSkip}
 *          1、获取候选的增强器（切面里面的通知方法），增强器类型：{@link InstantiationModelAwarePointcutAdvisor}
 *          2、判断增强器类型是否是{@link AspectJPointcutAdvisor}类型，如果是直接返回，默认情况返回False
 *  2、创建Bean对象，调用{@link AbstractAutoProxyCreator#postProcessAfterInitialization(Object, String)}方法实现创建代理对象
 *      1、判断是否已经有代理对象，如果有直接返回
 *      2、创建代理对象 {@link AbstractAutoProxyCreator#wrapIfNecessary(Object, String, Object)}
 *          1、获取当前类型能使用的增强器（通知方法） {@link AbstractAdvisorAutoProxyCreator#findEligibleAdvisors}
 *              1、找到所有候选的增强器{@link AbstractAdvisorAutoProxyCreator#findCandidateAdvisors}
 *              2、获取当前Bean能使用的增强器（找到哪些通知方法是需要切入当前Bean方法的）{@link AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply}
 *              3、给增强器排序{@link AbstractAdvisorAutoProxyCreator#sortAdvisors}
 *          2、保存当前Bean已被增强
 *          3、创建当前Bean的代理对象 {@link AbstractAutoProxyCreator#createProxy}
 *              1、获取所有增强器{@link AbstractAutoProxyCreator#buildAdvisors}
 *              2、保存到proxyFactory
 *              3、创建AOP代理对象：{@link DefaultAopProxyFactory#createAopProxy}
 *                  JdkDynamicAopProxy
 *                  ObjenesisCglibAopProxy
 *          4、将cglib增强后的Bean对象保存到容器中
 *          5、之后从容器中获取的Bean就是组件的代理对象
 * 3、目标对象的执行：容器中保存的是组件的代理对象（Cglib增强后的代理对象），代理对象保存了相关信息，如：增强器、目标对象等等。
 *      1、拦截目标方法的执行{@link CglibAopProxy.DynamicAdvisedInterceptor#intercept}
 *      2、获取ProxyFactory拦截器链（将通知方法包装成方法拦截器，利用MethodInterceptor机制） {@link AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice}
 *          1、获取拦截器列表，其中包含一个默认的拦截器 {@link DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice}
 *          2、遍历所有的增强器，将其转换为{@link Interceptor}类型
 *              1、将增强器转换为{@link MethodInterceptor}，
 *                  如果是MethodInterceptor类型直接加入集合
 *                  如果不是使用{@link AdvisorAdapter}对象将增强器转换为{@link MethodInterceptor}类型，加入集合，如：{@link BeforeAdvice}、{@link AfterReturningAdvice}
 *          3、返回拦截器数组
 *      3、如果没有拦截器，直接执行目标方法
 *      4、如果有拦截器，创建{@link CglibAopProxy.CglibMethodInvocation}对象
 *      5、并调用{@link CglibAopProxy.CglibMethodInvocation#proceed}方法返回结果
 *          1、如果没有拦截器，直接执行目标方法
 *          2、链式获取每一个拦截器，拦截器执行invoke方法，每一个拦截器等待下一个拦截器执行完成返回再执行自己的通知方法
 *             拦截器链机制，保证通知方法与目标方法的执行顺序
 *      6、处理结果{@link CglibAopProxy#processReturnType}
 *
 * 拦截器类型如下：
 * {@link ExposeInvocationInterceptor} 默认第一个拦截器
 * {@link AspectJAfterThrowingAdvice}
 * {@link AfterReturningAdviceInterceptor}
 * {@link AspectJAfterAdvice}
 * {@link MethodBeforeAdviceInterceptor}
 *
 * AOP总结：
 * 1、使用{@link EnableAspectJAutoProxy}注解开启AOP功能
 * 2、{@link EnableAspectJAutoProxy}注解会向容器注入组件{@link AnnotationAwareAspectJAutoProxyCreator}
 * 3、{@link AnnotationAwareAspectJAutoProxyCreator}组件是一个后置处理器
 *      1、registerBeanPostProcessors方法注册后置处理器，创建{@link AnnotationAwareAspectJAutoProxyCreator}组件
 *      2、finishBeanFactoryInitialization方法初始化剩下所有的单实例Bean
 *          1、创建业务逻辑组件和切面组件
 *          2、{@link AnnotationAwareAspectJAutoProxyCreator}拦截Bean的创建过程
 *          3、组件创建完成后，判断组件是否需要增强 {@link AbstractAutoProxyCreator#postProcessAfterInitialization(Object, String)}
 *             将切面的通知方法包装成增强器{@link Advisor}，给业务逻辑组件创建一个代理对象
 *  4、执行目标方法：
 *          1、代理对象执行目标方法
 *          2、执行代理对象的拦截器链
 *              1、得到目标方法的拦截器链（将增强器包装成拦截器{@link MethodInterceptor}）
 *              2、利用拦截器的链式机制，递归进入每个拦截器调用invoke方法
 *
 * @author Daniel
 */
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

    @Bean
    public MathCaculator mathCaculator() {
        return new MathCaculator();
    }

    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }

}
